﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.Security.Cryptography.Xml;

namespace SignMetadataForADFS
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine();
            Console.WriteLine(@"Enter the path of unsigned metadata (e.g., c:\metadata.xml that you want to use for signing...");
            var unsignedMetadataLocation = Console.ReadLine().Trim();
            Console.WriteLine();
            Console.WriteLine(@"Enter the path of certificate (e.g., c:\myCert.pfx that you want to use for signing...");
            var certLocation = Console.ReadLine().Trim();
            Console.WriteLine();
            Console.WriteLine("Enter the password for certificate");
            var certPassword = Console.ReadLine().Trim();
          
            Console.WriteLine();
            Console.WriteLine(@"Enter the path where you want to save signed metadata (e.g., c:\mymetadata.xml");
            var metadataLocation = Console.ReadLine().Trim();

            Console.WriteLine("Signing...");

            //Rahul: Get reference to the cert file
            X509Certificate2 certificate = new X509Certificate2(certLocation, certPassword);
            XmlDocument myDoc = new XmlDocument();
            myDoc.PreserveWhitespace = false;
            myDoc.Load(new XmlTextReader(unsignedMetadataLocation));

            //Rahul:port the xml document into a signed xml object type
            var signedXml = new SignedXml(myDoc);
            //Rahul:specify the key to be used for signing
            signedXml.SigningKey = certificate.PrivateKey;
            //Rahul:specify the referenceuri element of signature (should not be kept blank for ADFS)
            Reference myReference = new Reference();
            myReference.Uri = "";

            //Rahul:ADFS needs enveloped signature and not enveloping signature
            XmlDsigEnvelopedSignatureTransform envSig = new XmlDsigEnvelopedSignatureTransform();
            //Rahul:ADFS will reject the fed data if enveloped signature is last transform...so canonicalizing it
            XmlDsigExcC14NTransform transform = new XmlDsigExcC14NTransform();
            //Rahul: Add the above transforms to reference
            myReference.AddTransform(envSig);
            myReference.AddTransform(transform);

            //Rahul: Add reference to the signed xml which will ultimately sign
            signedXml.AddReference(myReference);

            //Rahul: Specify the keyinfo and canonicalization algo
            var ki = new KeyInfo();
            ki.AddClause(new KeyInfoX509Data(certificate));
            signedXml.KeyInfo = ki;
            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
            //Rahul: Sign the document
            signedXml.ComputeSignature();
            XmlElement xmlDigitalSignature = signedXml.GetXml();
            //Rahul: I am prepending signature. We can also append as well (to avoid clutter at start of token) which helps debugging
            myDoc.DocumentElement.PrependChild(xmlDigitalSignature);
            //Rahul: Removing that common xml declaration that is generated and which may invalidate the signature
            if (myDoc.FirstChild is XmlDeclaration)
            {
                myDoc.RemoveChild(myDoc.FirstChild);
            }
            //Rahul: Save the signed XML document to a file specified 
            XmlTextWriter writer = new XmlTextWriter(metadataLocation, new UTF8Encoding(false));
            myDoc.WriteTo(writer);
            writer.Close();
            Console.WriteLine("Signing complete!!!");
            Console.Read();
        }
    }
}
